spring 您所在的位置:网站首页 websocket 粘包 spring

spring

2023-08-30 14:27| 来源: 网络整理| 查看: 265

spring-boot 建socket服务器,处理物联网设备的粘包分包

首先dis一下某些物联网设备的通信协议。

您用TCP Socket协议也就罢了,可是您还通信协议只有开始符,没有结束符是怎么回事。 您还用不可见字符,二进制的,不便于调试。请问这都什么年代了,您的MCU(单片机)性能有那么弱吗? 来看一下通信协议:

通信帧格式: 名称 长度 说明 帧头 开始标识 2Byte 0xEF3A (高字节在前) 数据长度 2 Byte 长度是指信道、协议版本、命令、内容、CRC 五者的字节之和。(高字节在前) 信道 1 Byte 0x01 服务发出 4G/2G 0x02 设备发出 协议版本 1 Byte 当前版本 0x01 帧数据 命令 1 Byte 数据 N Byte 校验 CRC16 2 Byte 将开始标识、长度、信道、协议版本、命令和内容这六部分 内容的所有字节进行 CRC 计算(高字节在前)

别欺负我不懂MCU, 我以前也是干硬件出身,PCB也画过,MCU软件也写过,机器也量产过。 以上叨B叨那么多,硬件改不了,还是要对接啊。以下开始:

来人,上netty

image.png

其它的也就不説了,各种文章太多了, 来看看核心部分:

public class ServerChannelInitializer extends ChannelInitializer { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //这是用最简单的分割符来分割 //ByteBuf delimiter = Unpooled.copiedBuffer("#".getBytes()); //添加编解码 //socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); //socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); //前置,处理了普通分隔符作为报文结束符的分包和粘包问题,不过不适合此例子 //socketChannel.pipeline().addLast(new MyDelimiterBasedFrameDecoder(1024, delimiter)); socketChannel.pipeline().addLast("decoder", new MyBinDecoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast("encoder", new MyBinEncoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast(new NettyServerHandler()); } } 怎么写具体的协议解析

然后,就是我们定义的MyBinDecoder 最重要:

public class MyBinDecoder extends MessageToMessageDecoder { private final Charset charset; //上次的半包. 用于处理分包情况 private String lastHalfPacket = ""; public MyBinDecoder() { this(Charset.defaultCharset()); } public MyBinDecoder(Charset charset) { if (charset == null) { throw new NullPointerException("charset"); } else { this.charset = charset; } } protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { List cmdList = parseCmdList(msg); if(cmdList!=null && !cmdList.isEmpty()) { log.info("添加list"); out.addAll(cmdList); }else{ System.out.println("暂未发现指令"); } } byte[] starter = new byte[]{ (byte)0xEF, (byte)0x3A}; final int CMD_HEADER_LENGTH = 6; private List parseCmdList(ByteBuf originBuf){ ByteBuf buf = Unpooled.buffer(1024); buf.writeBytes(ByteBufUtil.decodeHexDump(lastHalfPacket)); buf.writeBytes(originBuf); List result = new ArrayList(); log.info("开始遍历 buf readableBytes=" + buf.readableBytes()); try { while(buf.readableBytes() >= CMD_HEADER_LENGTH) { byte[] bStart = new byte[2]; //String hex = ByteBufUtil.hexDump(b); buf.getBytes(buf.readerIndex(), bStart); log.info("读starter:" + ByteBufUtil.hexDump(bStart)); if (compareBytes(bStart, starter)) { log.info("找到了start"); //判断这次长度够不够读,如果不够读直接退出 //如果够读,读进来,插进cmd list byte[] bHeader = new byte[CMD_HEADER_LENGTH]; //buf.markReaderIndex(); //buf.readBytes(bHeader); buf.getBytes(buf.readerIndex(), bHeader); int dataLength = bHeader[2] * 256 + bHeader[3]; //buf.resetReaderIndex(); int THEORYLENGTH = 4 + dataLength; log.info("命令总长度: " + THEORYLENGTH); if(buf.readableBytes() >= THEORYLENGTH) { log.info("正在读全长"); byte[] bCmdFrame = new byte[THEORYLENGTH]; buf.readBytes(bCmdFrame); //readerIndex += THEORYLENGTH; String hexCmd = ByteBufUtil.hexDump(bCmdFrame); log.info("找到一个指令啦:" + hexCmd); result.add(hexCmd); log.info("读缓冲区还有:" + buf.readableBytes()); continue; }else{ log.info("命令总长未达预期,下次再读"); int halfPacketNum = buf.readableBytes(); byte[] bHalfPacket = new byte[halfPacketNum]; buf.readBytes(bHalfPacket); if(halfPacketNum>=1) { String halfPacketStr = ByteBufUtil.hexDump(bHalfPacket); log.info("剩下半包"+halfPacketStr); lastHalfPacket = halfPacketStr==null? "": halfPacketStr; } return result; } } else { //读指针向后移动一个 log.info("没找到starter,读指针向后移动一个"); buf.readBytes(1); continue; } } int halfPacketNum = buf.readableBytes(); if(halfPacketNum > 0) { log.info("HEADER命令头未达预期,下次再读"); byte[] bHalfPacket = new byte[halfPacketNum]; buf.readBytes(bHalfPacket); String halfPacketStr = ByteBufUtil.hexDump(bHalfPacket); log.info("剩下半包"+halfPacketStr); lastHalfPacket = halfPacketStr==null? "": halfPacketStr; }else{ log.info("余下字节恰好为0,本轮读完了"); } }catch (Exception e){ e.printStackTrace(); log.warn(e.toString()); return result; } return result; } private boolean compareBytes(byte[] b1, byte[] b2){ if(b1==null && b2==null){ return true; } if( (b1==null && b2!=null) || (b1!=null && b2==null)){ return false; } if(b1.length != b2.length){ return false; } for(int i = 0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有